#pragma once

#include <unordered_map>
#include <vector>
#include <string>
#include <assert.h>
#include <algorithm>

#include "../comm/utils.hpp"
#include "../comm/log.hpp"

namespace ns_model_file
{
    using namespace ns_log;
    using namespace ns_utils;
    using std::unordered_map;
    using std::vector;
    using std::string;

    struct Question
    {
        string number; // 题目编号
        string title; // 题目标题
        string star; // 题目难度等级，简单、中等、困难
        int cpu_limit; // cpu时间限制
        int mem_limit; // 内存空间限制
        string  desc; // 题目描述
        string basic_code; // 基本代码,预设在代码编辑器中的代码
        string test_code; // 测试代码
    };

    const string question_list = "./questions/questions.list";
    const string question_path = "./questions/";
    class Model
    {
    private:
        unordered_map<string,Question> _questions;
    public:
        Model()
        {
            assert(load_question_list(question_list));
        }
        /**
         * @brief 初始化所有题目信息到成员变量_questions中
         *
         * @param[in] question_list 题目列表文件
         *
         * @return true 或 false
         */
        bool load_question_list(const string& question_list)
        {
            std::ifstream in(question_list);
            if(!in.is_open())
            {
                LOG(FATAL) << "加载题目库失败，请检查题目库文件是否存在" << std::endl;

                return false;
            }

            string line_content; // 题库中某一行内容，包含某道题信息
            int line = 0; // 统计读到文件第几行
            while(getline(in,line_content))
            {
                line++;
                vector<string> v;
                StringUtil::split_string(line_content,&v,' ',true);
                if(v.size() != 5)
                {
                    LOG(WARING) << "加载第" << line << "行题目信息失败，请检查题目信息格式" << std::endl;
                    continue;
                }

                Question q;
                q.number = v[0];
                q.title = v[1];
                q.star = v[2];
                q.cpu_limit = std::stoi(v[3]);
                q.mem_limit = std::stoi(v[4]);

                string path = question_path;
                path += q.number;
                path += "/";

                FileUtil::read_file(path+"desc.txt",&q.desc,true);
                FileUtil::read_file(path+"basic_code.cpp",&q.basic_code,true);
                FileUtil::read_file(path+"test_code.cpp",&q.test_code,true);

                _questions.insert({q.number,q});
            }
            LOG(INFO) << "加载题库成功" << std::endl;
            in.close();

            return true;
        }

        /**
         * @brief 获取所有题目信息到容器中
         *
         * @param[out] questions 存放题目信息的容器
         *
         * @return true 或 false
         */
        bool get_all_questions(vector<Question>* questions)
        {
            if(_questions.size() == 0)
            {
                LOG(ERROR) << "获取题库失败" << std::endl;

                return false;
            }

            for(const auto& e : _questions)
            {
                questions->push_back(e.second); // first: key, second: value
            }

            /* 按题号对所有题目排序 */
            sort(questions->begin(), questions->end(), [](const struct Question &q1, const struct Question &q2){
                return stoi(q1.number) < stoi(q2.number);
            });

            return true;
        }

        /**
         * @brief 获取题目编号对应题目信息
         *
         * @param[in] 题目编号
         *
         * @param[out] question 存放题目信息
         *
         * @return true 或 false
         */
        bool get_one_question(const string& number,Question* question)
        {
            const auto iter = _questions.find(number);
            if(iter == _questions.end())
            {
                LOG("获取题目失败，题目编号为：") << number << std::endl;

                return false;
            }
            *question = iter->second;

            return true;
        }
    };
}
